<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>View Transition API FLIP动画示例集合</title>
    <style>
        *:not(dialog) { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
        body { padding: 20px; max-width: 1200px; margin: 0 auto; /*background-color: #f5f5f5;*/ }
        header { text-align: center; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 1px solid #ddd; }
        h1 { color: #333; margin-bottom: 10px; }
        .description { color: #666; max-width: 800px; margin: 0 auto; }
        .example-container { background-color: white; border-radius: 8px; padding: 25px; margin-bottom: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        .example-title { color: #444; margin-bottom: 20px; display: flex; align-items: center; gap: 10px; }
        .example-title i { color: #2c3e50; }
        .controls { margin-bottom: 20px; display: flex; gap: 10px; flex-wrap: wrap; }
        button { background-color: #2c3e50; color: white; border: none; padding: 10px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s; }
        button:hover { background-color: #34495e; }

        /* 示例1: 简单方块变换 */
        #box-container {
            width: 100%;
            height: 200px;
            position: relative;
            border: 1px solid #eee;
            border-radius: 4px;
            overflow: hidden;
        }

        .box {
            width: 100px;
            height: 100px;
            background-color: #3498db;
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
            position: absolute;
            top: 50px;
            left: 50px;
            transition: all 0.3s;
            view-transition-name: moving-box;
        }

        /* 示例2: 卡片网格重排 */
        .card-grid {
            display: grid;
						grid: repeat(6,50px)/auto;
            gap: 15px;
            margin-top: 20px;
        }
				.card-grid.h{
					grid: auto/repeat(auto-fill, minmax(150px, 1fr));
				}
				.card-grid.h .card{
					aspect-ratio: 3/2;
				}
        .card {
            background-color: #ecf0f1;
            border-radius: 8px;
            padding: 15px;
            text-align: center;
            cursor: pointer;
            transition: transform 0.2s;
            display: flex;
            flex-direction: column;
            justify-content: center;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }

        .card:hover {
            transform: translateY(-3px);
        }

        .card.large {
            grid-column: span 2;
            grid-row: span 2;
            background-color: #9b59b6;
            color: white;
        }

        /* 示例3: 列表到网格的转换 */
        .list-view {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        .grid-view {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 15px;
        }

        .item {
            background-color: #2ecc71;
            color: white;
            padding: 15px;
            border-radius: 6px;
        }

        /* 示例4: 优化的模态框过渡 */
        .modal-trigger {
            background-color: #1a73e8;
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 6px;
            font-size: 16px;
            cursor: pointer;
            transition: background-color 0.2s;
            view-transition-name: modal-trigger;
        }

        .modal-trigger:hover {
            background-color: #0d66d0;
        }

        .modal-backdrop {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            opacity: 0;
            view-transition-name: modal-backdrop;
        }

        .modal {
            background-color: white;
            border-radius: 12px;
            width: 90%;
            max-width: 500px;
            padding: 30px;
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
            transform: scale(0.8);
            opacity: 0;
            view-transition-name: modal-content;
        }

        .modal h2 {
            color: #202124;
            margin-bottom: 15px;
            font-size: 24px;
        }

        .modal p {
            color: #5f6368;
            margin-bottom: 25px;
            line-height: 1.6;
        }

        .modal-actions {
            display: flex;
            justify-content: flex-end;
        }

        .modal-close {
            background-color: #1a73e8;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }
				
				/* 示例5: 图片画廊 */
				.gallery {
					display: grid;
					grid: auto/repeat(3, 1fr);
					gap: 15px;
				}
    
				.gallery img {
					width: 100%;
					height: 200px;
					object-fit: cover;
					cursor: pointer;
					contain: layout;
				}
				
				.gallery img.fullscreen {
					position: fixed;
					top: 0;
					left: 0;
					width: 100%;
					height: 100%;
					object-fit: contain;
					background: rgba(0, 0, 0, 0.9);
					z-index: 100;
				}
    
				/* 示例6: 自定义动画 */
				#custom-animation {
					background-color: #fff;
				}
				
				#custom-box {
					width: 120px;
					height: 60px;
					background-color: #9b59b6;
					display: flex;
					align-items: center;
					justify-content: center;
					color: white;
					font-weight: bold;
					border-radius: 8px;
					cursor: pointer;
					box-shadow: 0 3px 6px rgba(0,0,0,0.16);
					margin: 20px 0;
				}
				
				/* 示例7: 列表删除动画 */
				.demo7-list{
					display: grid;
					gap: 20px;
					grid-template-columns: repeat(4, 1fr);
					grid-auto-flow: row dense;
				}
				.demo7-item{
					width: 100px;
					aspect-ratio: 1/1;
					background: royalblue;
					color: #fff;
					font-size: 30px;
					border-radius: 10px;
					display: flex;
					align-items: center;
					justify-content: center;
					cursor: pointer;
				}
				.demo7-item{
					view-transition-name: var(--i);
				}
				
				/* 示例8: 弹窗从按钮呼出 */
				.demo8 {
					
				}
				.demo8 button{
					padding: 5px 16px;
					background-color: transparent;
					border-radius: 8px;
					line-height: 1.4;
					box-shadow: 0 2px #00000004;
					cursor: pointer;
					transition: .3s;
					transform: scale(1);
					border-color: transparent;
					background-color: royalblue;
					color: #fff;
					text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
					box-shadow: 0 2px #0000000b;
				}
				.demo8 button:hover{
					filter: brightness(1.1);
				}

				.demo8 .bnt-group{
					display: flex;
					gap: 100px;
					margin-top: 50px;
				}
				.demo8 dialog{
					width: 300px;
					height: 200px;
					view-transition-name: dialog;
				}
				::view-transition-old(dialog) {
					display: none;
				}
				
				/* 示例9: 切换主题动画 */
				.demo9 {
					background-color: transparent;
				}
				.demo9 button{
					padding: 5px 16px;
					background-color: transparent;
					border-radius: 8px;
					line-height: 1.4;
					box-shadow: 0 2px #00000004;
					cursor: pointer;
					transition: .3s;
					transform: scale(1);
					border-color: transparent;
					background-color: royalblue;
					color: #fff;
					text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
					box-shadow: 0 2px #0000000b;
				}
				.dark{
					background-color: #111;
					color-scheme: dark;
				}
				.demo9 button:hover{
					filter: brightness(1.1);
				}
				.demo9-list{
					display: grid;
					grid-template-columns: repeat(3, 1fr);
					gap: 15px;
					margin-top: 20px;
				}
				.demo9-item{
					width: 200px;
					height: 150px;
					background-color: royalblue;
					border-radius: 10px;
				}
				::view-transition-old(root) {
					animation: none;
					mix-blend-mode: normal;
				}
				::view-transition-new(root) {
					mix-blend-mode: normal;
					animation: clip 1s ease-in;
				}
				@keyframes clip {
					from {
						clip-path: circle(0% at var(--x) var(--y));
					}
					to{
						clip-path: circle(100% at var(--x) var(--y));
					}
				}
    
				/* 视图过渡动画 */
				/* ::view-transition {
					pointer-events: none;
				} */
				
				::view-transition-group(full-image) {
					animation-duration: 0.5s;
					animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
				} 
				
				::view-transition-group(custom-box) {
					animation-duration: 0.5s;
					animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
				} 

        /* 定义视图过渡动画 */
        @view-transition {
            navigation: auto;
        }

        /* 方块动画 */
        ::view-transition-old(moving-box),
        ::view-transition-new(moving-box) {
            animation-duration: 0.5s;
        }

        /* 模态框动画 */
        ::view-transition-old(modal-backdrop),
        ::view-transition-new(modal-backdrop) {
            animation-duration: 0.4s;
        }

        ::view-transition-old(modal-content),
        ::view-transition-new(modal-content) {
            animation-duration: 0.4s;
            animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
        }

        ::view-transition-new(modal-backdrop) {
            animation-name: backdrop-fade-in;
        }

        ::view-transition-old(modal-backdrop) {
            animation-name: backdrop-fade-out;
        }

        ::view-transition-new(modal-content) {
            animation-name: modal-scale-in;
        }

        ::view-transition-old(modal-content) {
            animation-name: modal-scale-out;
        }

        @keyframes backdrop-fade-in {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        @keyframes backdrop-fade-out {
            from { opacity: 1; }
            to { opacity: 0; }
        }

        @keyframes modal-scale-in {
            from { 
                transform: scale(0.8);
                opacity: 0;
            }
            to { 
                transform: scale(1);
                opacity: 1;
            }
        }

        @keyframes modal-scale-out {
            from { 
                transform: scale(1);
                opacity: 1;
            }
            to { 
                transform: scale(0.8);
                opacity: 0;
            }
        }
    </style>
</head>
<body>
    <header>
        <h1>View Transition API 实现 FLIP 动画</h1>
        <p class="description">
            下面是几个使用 View Transition API 实现 FLIP (First, Last, Invert, Play) 动画效果的示例。
            FLIP 动画技术通过记录元素的初始状态和最终状态，计算出变换差异，然后反向应用这个差异来创建平滑的过渡效果。
						示例太多会影响性能，View Transition是否没有原始的FLIP动画性能好？
        </p>
    </header>

    <!-- 示例1: 简单方块位置和大小变换 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-cube"></i> 示例1: 方块位置和大小变换
        </h2>
        <div class="controls">
            <button onclick="moveBox('top-right')">移动到右上角</button>
            <button onclick="moveBox('bottom-center')">移动到底部中央</button>
            <button onclick="moveBox('original')">回到原始位置</button>
            <button onclick="toggleBoxSize()">切换大小</button>
        </div>
        <div id="box-container">
            <div id="animated-box" class="box">我是方块</div>
        </div>
    </div>

    <!-- 示例2: 卡片网格重排 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-th"></i> 示例2: 卡片网格重排
        </h2>
        <div class="controls">
            <button onclick="rearrangeCards()">重排卡片</button>
            <button onclick="toggleFeaturedCard()">切换特色卡片</button>
						<button onclick="toggleViewH()">切换横排或竖排</button>
        </div>
        <div id="card-container" class="card-grid">
            <div class="card" data-id="1">卡片 1</div>
            <div class="card" data-id="2">卡片 2</div>
            <div class="card" data-id="3">卡片 3</div>
            <div class="card" data-id="4">卡片 4</div>
            <div class="card" data-id="5">卡片 5</div>
            <div class="card" data-id="6">卡片 6</div>
        </div>
    </div>

    <!-- 示例3: 列表到网格的转换 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-exchange-alt"></i> 示例3: 列表与网格视图切换
        </h2>
        <div class="controls">
            <button onclick="toggleView()">切换视图</button>
        </div>
        <div id="view-container" class="list-view">
            <div class="item" data-id="1">项目 1</div>
            <div class="item" data-id="2">项目 2</div>
            <div class="item" data-id="3">项目 3</div>
            <div class="item" data-id="4">项目 4</div>
            <div class="item" data-id="5">项目 5</div>
        </div>
    </div>

    <!-- 示例4: 优化的模态框过渡 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-window-maximize"></i> 示例4: 模态框过渡
        </h2>
        <div class="controls">
            <button class="modal-trigger" onclick="openModal()">打开模态框</button>
        </div>
        <div id="modal" class="modal-backdrop">
            <div class="modal">
                <h2>这是一个模态框</h2>
                <p>这个模态框使用 View Transition API 实现了平滑的过渡效果。你可以看到明显的淡入淡出和缩放动画，让模态框的显示和隐藏更加自然。</p>
                <div class="modal-actions">
                    <button class="modal-close" onclick="closeModal()">关闭</button>
                </div>
            </div>
        </div>
    </div>
		
		<!-- 示例5: 图片放大/缩小 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-window-maximize"></i> 示例5: 图片放大/缩小
        </h2>
        <div class="controls">
					<p>点击图片查看全屏/缩放的平滑过渡效果</p>
        </div>
        <div class="gallery">
					<img src="https://picsum.photos/id/10/300/200" alt="森林">
					<img src="https://picsum.photos/id/11/300/200" alt="山脉">
					<img src="https://picsum.photos/id/12/300/200" alt="海岸">
				</div>
    </div>
		
		<!-- 示例6: 自定义动画效果 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-window-maximize"></i> 示例6: 自定义动画效果
        </h2>
        <div class="controls">
					<p>点击下方方块查看自定义的圆形展开动画效果</p>
        </div>
        <div id="custom-box">点击我</div>
    </div>
		
		<!-- 示例7: 列表删除动画 -->
    <div class="example-container">
        <h2 class="example-title">
            <i class="fas fa-window-maximize"></i> 示例7: 列表删除动画
        </h2>
        <div class="controls">
					<p>点击删除</p>
        </div>
				<div class="demo7-list" id="demo7list">
					<div class="demo7-item" style="--i: a1">1</div>
					<div class="demo7-item" style="--i: a2">2</div>
					<div class="demo7-item" style="--i: a3">3</div>
					<div class="demo7-item" style="--i: a4">4</div>
					<div class="demo7-item" style="--i: a5">5</div>
					<div class="demo7-item" style="--i: a6">6</div>
					<div class="demo7-item" style="--i: a7">7</div>
					<div class="demo7-item" style="--i: a8">8</div>
					<div class="demo7-item" style="--i: a9">9</div>
					<div class="demo7-item" style="--i: a10">10</div>
				</div>
    </div>
		
		<!-- 示例8: 弹窗从按钮呼出动画 -->
    <div class="example-container demo8">
        <h2 class="example-title">
            <i class="fas fa-window-maximize"></i> 示例8: 弹窗从按钮呼出动画
        </h2>
        <div class="controls">
					<p>点击按钮弹窗从按钮弹出</p>
        </div>
        <div class="bnt-group" id="demo8group">
					<button>按钮1</button>
					<button>按钮2</button>
					<button>按钮3</button>
				</div>
				<dialog id="dialog">
					<form method="dialog">
						我是弹窗
						<button>关闭</button>
					</form>
				</dialog>
    </div>
		
		<!-- 示例9: 切换主题动画 -->
    <div class="example-container demo9">
        <h2 class="example-title">
            <i class="fas fa-window-maximize"></i> 示例9: 切换主题动画
        </h2>
        <div class="controls" style="justify-content: center;">
					<button id="demo9btn">切换</button>
        </div>
				<div class="demo9-list">
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
					<div class="demo9-item"></div>
				</div>
    </div>

    <script>
        // 示例1: 方块位置和大小变换
        const box = document.getElementById('animated-box');
        let isLarge = false;

        function moveBox(position) {
            // 启动视图过渡
            document.startViewTransition(() => {
                switch(position) {
                    case 'top-right':
                        box.style.top = '20px';
                        box.style.left = 'calc(100% - 150px)';
                        break;
                    case 'bottom-center':
                        box.style.top = 'calc(100% - 120px)';
                        box.style.left = 'calc(50% - 50px)';
                        break;
                    case 'original':
                        box.style.top = '50px';
                        box.style.left = '50px';
                        break;
                }
            });
        }

        function toggleBoxSize() {
            document.startViewTransition(() => {
                isLarge = !isLarge;
                if (isLarge) {
                    box.style.width = '150px';
                    box.style.height = '150px';
                    box.style.backgroundColor = '#e74c3c';
                } else {
                    box.style.width = '100px';
                    box.style.height = '100px';
                    box.style.backgroundColor = '#3498db';
                }
            });
        }

        // 示例2: 卡片网格重排
        const cardContainer = document.getElementById('card-container');
        const cards = Array.from(cardContainer.children);

        function rearrangeCards() {
            document.startViewTransition(() => {
                // 移除所有卡片
                cards.forEach(card => cardContainer.removeChild(card));
                
                // 随机排序
                const shuffled = [...cards].sort(() => Math.random() - 0.5);
                
                // 重新添加卡片
                shuffled.forEach(card => {
                    card.classList.remove('large');
                    cardContainer.appendChild(card);
                });
            });
        }

        function toggleFeaturedCard() {
            document.startViewTransition(() => {
                // 移除所有卡片的large类
                cards.forEach(card => card.classList.remove('large'));
                
                // 随机选择一张卡片作为特色卡片
                const randomIndex = Math.floor(Math.random() * cards.length);
                cards[randomIndex].classList.add('large');
            });
        }

        // 示例3: 列表到网格的转换
        const viewContainer = document.getElementById('view-container');
        let isGridView = false;

        function toggleView() {
            document.startViewTransition(() => {
                isGridView = !isGridView;
                if (isGridView) {
                    viewContainer.classList.remove('list-view');
                    viewContainer.classList.add('grid-view');
                } else {
                    viewContainer.classList.remove('grid-view');
                    viewContainer.classList.add('list-view');
                }
            });
        }
				
				function toggleViewH() {
            document.querySelector('.card-grid').classList.toggle('h');
        }

        // 示例4: 优化的模态框过渡
        const modal = document.getElementById('modal');

        function openModal() {
            // 启动视图过渡
            document.startViewTransition(() => {
                // 在过渡回调中修改DOM
                modal.style.display = 'flex';
                // 触发重排后设置最终状态
                setTimeout(() => {
                    modal.querySelector('.modal').style.transform = 'scale(1)';
                    modal.querySelector('.modal').style.opacity = '1';
                    modal.style.opacity = '1';
                }, 10);
            });
        }

        function closeModal() {
            // 启动视图过渡
            document.startViewTransition(() => {
                // 在过渡回调中修改DOM到关闭状态
                modal.querySelector('.modal').style.transform = 'scale(0.8)';
                modal.querySelector('.modal').style.opacity = '0';
                modal.style.opacity = '0';
                
                // 动画完成后隐藏
                setTimeout(() => {
                    modal.style.display = 'none';
                }, 400);
            });
        }

				// 示例5: 图片放大/缩小效果
				document.querySelectorAll('.gallery img').forEach(img => {
					img.addEventListener('click', async () => {
						if (!document.startViewTransition) {
							img.classList.toggle('fullscreen');
							return;
						}
						
						img.style.viewTransitionName = 'full-image';
						const transition = document.startViewTransition(() => {
							img.classList.toggle('fullscreen');
						});
						
						try {
							await transition.ready;
						} finally {
							img.style.viewTransitionName = '';
						}
					});
				});
    
				// 示例6: 自定义动画效果
				const customBox = document.getElementById('custom-box');
				customBox.addEventListener('click', async (event) => {
					if (!document.startViewTransition) {
						customBox.textContent = customBox.textContent === '点击我' ? '动画完成!' : '点击我';
						return;
					}
					
					customBox.style.viewTransitionName = 'custom-box';
					const transition = document.startViewTransition(() => {
						customBox.textContent = customBox.textContent === '点击我' ? '动画完成!' : '点击我';
						customBox.style.backgroundColor = customBox.textContent === '动画完成!' ? '#e67e22' : '#9b59b6';
					});
					
					await transition.ready;
					
					// 自定义动画
					document.documentElement.animate(
						[
							{ 
								clipPath: `circle(0% at ${event.clientX}px ${event.clientY}px)`,
								opacity: 0 
							},
							{ 
								clipPath: `circle(150% at ${event.clientX}px ${event.clientY}px)`,
								opacity: 1 
							}
						],
						{
							duration: 600,
							pseudoElement: '::view-transition-new(custom-box)'
						}
					);
					
					customBox.style.viewTransitionName = '';
				});
				
				//示例7: 列表删除动画
				demo7list.addEventListener('click', function(ev){
					if (ev.target.className === 'demo7-item') {
						if (document.startViewTransition) {
							document.startViewTransition(() => {
								ev.target.remove()
							});
						} else {
							ev.target.remove()
						}
					}
				})
				
				//示例8: 列表删除动画
				demo8group.addEventListener('click', function(ev){
					if (ev.target.tagName === 'BUTTON') {
						ev.target.style.viewTransitionName = 'dialog'
						if (document.startViewTransition) {
							document.startViewTransition(() => {
								dialog.showModal()
								ev.target.style.viewTransitionName = ''
							});
						} else {
							dialog.showModal()
						}
					}
				})
				
				//示例9: 主题切换动画
				demo9btn.addEventListener('click', function(ev){
					document.documentElement.style.setProperty('--x', ev.clientX + 'px')
					document.documentElement.style.setProperty('--y', ev.clientY + 'px')
					if (document.startViewTransition) {
						document.startViewTransition(() => {
							document.documentElement.classList.toggle('dark')
						});
					} else {
						document.documentElement.classList.toggle('dark')
					}
				})
				
				
				// 为所有项目设置唯一的view-transition-name
        document.querySelectorAll('.item').forEach((item, index) => {
            item.style.viewTransitionName = `item-${item.dataset.id}`;
        });

        // 为所有卡片设置唯一的view-transition-name
        document.querySelectorAll('.card').forEach(card => {
            card.style.viewTransitionName = `card-${card.dataset.id}`;
        });
    </script>
</body>
</html>
